﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace ToolKit.Mvvm.Events
{
    /// <summary>
    /// 事件总线
    /// </summary>
    public class EventBus
    {
        public static EventBus Default { get; } = new EventBus();

        private readonly ConcurrentDictionary<Type, Delegate> _eventMaps;

        public EventBus()
        {
            _eventMaps = new ConcurrentDictionary<Type, Delegate>();
        }

        /// <summary>
        /// 订阅事件
        /// </summary>
        /// <typeparam name="TMessage"></typeparam>
        /// <param name="callback"></param>
        public void Subscribe<TMessage>(Action<object, TMessage> callback) where TMessage : class
        {
            Type eventType = typeof(TMessage);

            if (_eventMaps.TryGetValue(eventType, out Delegate @delegates))
            {
                _eventMaps[eventType] = Delegate.Combine(@delegates, callback);
            }
            else
            {
                _eventMaps.TryAdd(eventType, callback);
            }

        }

        /// <summary>
        /// 取消订阅
        /// </summary>
        /// <typeparam name="TMessage"></typeparam>
        /// <param name="callback"></param>
        /// <returns></returns>
        public bool UnSubscribe<TMessage>(Action<object, TMessage> callback) where TMessage : class
        {
            Type eventType = typeof(TMessage);

            if (_eventMaps.ContainsKey(eventType))
            {
                return _eventMaps.TryRemove(eventType,out Delegate @delegate);
            }
            return false;
        }

        /// <summary>
        /// 发布事件
        /// </summary>
        /// <typeparam name="TMessage"></typeparam>
        /// <param name="message"></param>
        public void Publish<TMessage>(object sender, TMessage message) where TMessage : class
        {
            var eventType = typeof(TMessage);
            if (_eventMaps.TryGetValue(eventType, out Delegate @delegates))
            {
                foreach (var item in @delegates.GetInvocationList())
                {
                    if(item is Action<object, TMessage> handler)
                    {
                        handler?.Invoke(sender, message);
                    }                    
                }
            }

        }
    }
}
